package com.csw.android.doubanslidedemo.slide.view;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewParent;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.Scroller;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;

import com.csw.android.doubanslidedemo.R;

import java.util.ArrayList;
import java.util.List;

public class DoubanSlideView extends FrameLayout {
    private String flingScrollableTag = getContext().getString(R.string.DoubanSlideView_FlingScrollableElement);
    private String tabTag = getContext().getString(R.string.DoubanSlideView_TabTag);
    private int fgMinYOffset = 0;
    private int fgMaxYOffset = 0;
    private int fgYOffset = -1;
    private int fgTopArea = 2 * getResources().getDimensionPixelSize(R.dimen.dp48);
    private View bgView;
    private View fgView;

    public DoubanSlideView(@NonNull Context context) {
        super(context);
        init();
    }

    public DoubanSlideView(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public DoubanSlideView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private FlingHandler flingHandler = null;
    private TabHandler tabHandler = null;

    private void init() {

    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        bgView = getChildAt(0);
        fgView = getChildAt(1);
        if (bgView == null || fgView == null) {
            throw new RuntimeException("请在布局文件中添加两个子View");
        }
        LayoutParams lp = (LayoutParams) bgView.getLayoutParams();
        lp.bottomMargin = fgTopArea;
        bgView.setLayoutParams(lp);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        fgMaxYOffset = h - fgTopArea;
        if (fgYOffset == -1) {
            fgYOffset = fgMaxYOffset;
        }
        if (fgYOffset > fgMaxYOffset) {
            fgYOffset = fgMaxYOffset;
        } else if (fgYOffset < 0) {
            fgYOffset = fgMinYOffset;
        }
        updateChildOffset();
    }

    private boolean scrollFormBgView = false;


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//            Log.d("dispatchTouchEvent", "ACTION_DOWN_" + fgView.canScrollVertically(1) + "," + fgView.canScrollVertically(-1));
            if (flingHandler != null) {
                flingHandler.end();
                flingHandler = null;
            }
//            if (tabHandler != null) {
//                tabHandler.end();
//                tabHandler = null;
//            }
        }
        return super.dispatchTouchEvent(ev);
    }

    //私有方法<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


    /**
     * 更新child偏移量
     */
    private void updateChildOffset() {
        if (fgView.getTranslationY() != fgYOffset) {
            fgView.setTranslationY(fgYOffset);
            if (bgView.getTranslationY() < fgYOffset - fgMaxYOffset) {
                //bgView与fgView之间有间隔，消除间隔
                bgView.setTranslationY(fgYOffset - fgMaxYOffset);
            } else if (bgViewIsInEnd()) {
                //fgView偏移的时候，bgView内部已经到底了（通过不能上划判断），则让bgView跟随fgView往上
                bgView.setTranslationY(fgYOffset - fgMaxYOffset);
            }
        }
    }

    /**
     * 判断bgView是否已经滚动到末尾了
     */
    private boolean bgViewIsInEnd() {
        View scrollChild = bgView.findViewWithTag(flingScrollableTag);
        return !scrollChild.canScrollVertically(1);
    }

    /**
     * 判断视图来自于哪个子视图
     */
    private View getFromChild(View target) {
        ViewParent viewParent = target.getParent();
        if (viewParent == this) {
            return target;
        } else if (viewParent instanceof View) {
            return getFromChild((View) viewParent);
        } else {
            return null;
        }
    }

    private void addScrollChildTargetToMyChild(ArrayList<View> list, View target, int direction) {
        if (target == this) {
            return;
        }
        if (target.canScrollVertically(direction)) {
            list.add(target);
        }
        ViewParent vp = target.getParent();
        if (vp instanceof View) {
            addScrollChildTargetToMyChild(list, (View) vp, direction);
        }
    }

    private void addScrollChildMyChildToTarget(ArrayList<View> list, View target, int direction) {
        if (target == this) {
            return;
        }
        ViewParent vp = target.getParent();
        if (vp instanceof View) {
            addScrollChildTargetToMyChild(list, (View) vp, direction);
        }
        if (target.canScrollVertically(direction)) {
            list.add(target);
        }
    }
    //私有方法>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    //嵌套滑动<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    @Override
    public int getNestedScrollAxes() {
        //处理竖直方向的嵌套滑动
        return ViewCompat.SCROLL_AXIS_VERTICAL;
    }

    @Override
    public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
    }

    @Override
    public void onNestedScrollAccepted(View child, View target, int axes) {
        super.onNestedScrollAccepted(child, target, axes);
    }

    @Override
    public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
        Log.d("onNestedPreScroll", "" + dx + " | " + dy);
        int xc = consumed[0];
        int yc = consumed[1];
        int xu = dx;
        int yu = dy;
        if (yu != 0) {
            //fgView处于半显示中，优先偏移fgView到显示或者隐藏
            if (fgYOffset > fgMinYOffset && fgYOffset < fgMaxYOffset) {
                int destOffset = fgYOffset - yu;
                if (destOffset < fgMinYOffset) {
                    destOffset = fgMinYOffset;
                } else if (destOffset > fgMaxYOffset) {
                    destOffset = fgMaxYOffset;
                }
                int c = fgYOffset - destOffset;
                fgYOffset = destOffset;
                yc += c;
                yu -= c;
                updateChildOffset();
            }
        }
        consumed[0] = xc;
        consumed[1] = yc;
        super.onNestedPreScroll(target, xu, yu, consumed);
    }


    @Override
    public void onNestedScroll(View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
        Log.d("onNestedScroll", "" + dyConsumed + " | " + dyUnconsumed);
        int xc = dxConsumed;
        int yc = dyConsumed;
        int xu = dxUnconsumed;
        int yu = dyUnconsumed;

        if (yu != 0) {
            int destOffset = fgYOffset - yu;
            if (destOffset < fgMinYOffset) {
                destOffset = fgMinYOffset;
            } else if (destOffset > fgMaxYOffset) {
                destOffset = fgMaxYOffset;
            }
            int c = fgYOffset - destOffset;
            fgYOffset = destOffset;
            yc += c;
            yu -= c;
            updateChildOffset();
        }
        super.onNestedScroll(target, xc, yc, xu, yu);
    }

    @Override
    public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
        Log.d("onNestedPreFling", "" + velocityX + " | " + velocityY);
        if (Math.abs(velocityY) > Math.abs(velocityX)) {
            //竖直方向的快速滑动
            if (flingHandler != null) {
                flingHandler.end();
            }
            int direction = velocityY > 0 ? 1 : -1;
            ArrayList<View> scrollViews = new ArrayList<>();
            if (getFromChild(target) == fgView) {
                if (target.getTag() == tabTag) {
                    if (bgViewIsInEnd()) {
                        //bgView已经滚动到底
                        scrollViews.add(DoubanSlideView.this);
                        View scrollChild = bgView.findViewWithTag(flingScrollableTag);
                        if (scrollChild != null) {
                            addScrollChildMyChildToTarget(scrollViews, scrollChild, direction);
                        }
                        flingHandler = new FlingHandler(getContext(), target, scrollViews);
                        flingHandler.decelerateInEnd = true;
                        flingHandler.startFling(velocityX, velocityY);
                    } else {
                        //bgView没到底
                        //根据滑动方向显示或关闭fgView
                        if (tabHandler != null) {
                            tabHandler.end();
                        }
                        tabHandler = new TabHandler(getContext());
                        int dy = velocityY < 0 ? fgMaxYOffset - fgYOffset : fgMinYOffset - fgYOffset;
                        tabHandler.startScroll(dy);
                    }
                    return true;
                } else if (target.getTag() == flingScrollableTag) {
                    if (bgViewIsInEnd()) {
                        //bgView已经滚动到底
                        addScrollChildTargetToMyChild(scrollViews, target, direction);
                        scrollViews.add(DoubanSlideView.this);
                        View scrollChild = bgView.findViewWithTag(flingScrollableTag);
                        if (scrollChild != null) {
                            addScrollChildMyChildToTarget(scrollViews, scrollChild, direction);
                        }
                        flingHandler = new FlingHandler(getContext(), target, scrollViews);
                        flingHandler.decelerateInEnd = true;
                        flingHandler.startFling(velocityX, velocityY);
                    } else {
                        //bgView没到底
                        int targetOffsetY = target.getScrollY();
                        if (target instanceof MyRecyclerView) {
                            targetOffsetY = ((MyRecyclerView) target).getScrollY2();
                        }
                        if (targetOffsetY == 0) {
                            //fgView处于顶部
                            if (direction > 0) {
                                //上划，滚动fg内部
                                scrollViews.add(target);
                                flingHandler = new FlingHandler(getContext(), target, scrollViews);
                                flingHandler.startFling(velocityX, velocityY);
                            } else {
                                //下划，将fgView向关闭位置偏移
                                if (tabHandler != null) {
                                    tabHandler.end();
                                }
                                tabHandler = new TabHandler(getContext());
                                int dy = fgMaxYOffset - fgYOffset;
                                tabHandler.startScroll(dy);
                            }
                        } else {
                            //滚动fg内部
                            scrollViews.add(target);
                            flingHandler = new FlingHandler(getContext(), target, scrollViews);
                            flingHandler.startFling(velocityX, velocityY);
                        }
                    }
                    return true;
                }
            } else if (getFromChild(target) == bgView) {
                if (target.getTag() == flingScrollableTag) {
                    if (direction > 0) {
                        addScrollChildTargetToMyChild(scrollViews, target, direction);
                        scrollViews.add(DoubanSlideView.this);
                    } else {
                        scrollViews.add(DoubanSlideView.this);
                        addScrollChildMyChildToTarget(scrollViews, target, direction);
                    }
                    flingHandler = new FlingHandler(getContext(), target, scrollViews);
                    flingHandler.startFling(velocityX, velocityY);
                    return true;
                }
            }
        }
        return super.onNestedPreFling(target, velocityX, velocityY);
    }

    @Override
    public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        return super.onNestedFling(target, velocityX, velocityY, consumed);
    }

    @Override
    public void onStopNestedScroll(View child) {
        super.onStopNestedScroll(child);
        //嵌套滚动已经停止
        if (getFromChild(child) == fgView) {
            if (tabHandler != null) {
                //这里在执行快速滑动带来的fgview复位
                if (!tabHandler.isEnd) {
                    return;
                }
            }
            if (!bgViewIsInEnd()) {
                if (fgYOffset > fgMinYOffset && fgYOffset < fgMaxYOffset) {
                    tabHandler = new TabHandler(getContext());
                    int dy = fgYOffset > (fgMaxYOffset + fgMinYOffset) / 2 ? fgMaxYOffset - fgYOffset : fgMinYOffset - fgYOffset;
                    tabHandler.startScroll(dy);
                }
            }
        }
    }

    //嵌套滑动>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    private class TabHandler {
        private final Scroller scroller;
        private boolean isEnd = false;
        private final Runnable updateRunnable = new Runnable() {
            @Override
            public void run() {
                if (!isEnd) {
                    if (scroller.computeScrollOffset()) {
                        fgYOffset = scroller.getCurrY();
                        if (fgYOffset < fgMinYOffset) {
                            fgYOffset = fgMinYOffset;
                        } else if (fgYOffset > fgMaxYOffset) {
                            fgYOffset = fgMaxYOffset;
                        }
                        updateChildOffset();
                        post(this);
                    } else {
                        end();
                    }
                }
            }
        };

        public TabHandler(Context context) {
            scroller = new Scroller(context, new AccelerateDecelerateInterpolator());
        }

        public void startScroll(int dy) {
            scroller.startScroll(
                    0,
                    fgYOffset,
                    0,
                    dy,
                    300
            );
            post(updateRunnable);
        }

        public void end() {
            isEnd = true;
        }
    }

    private class FlingHandler {
        private boolean isEnd = false;
        private final Scroller scroller;
        private final ArrayList<View> scrollViews = new ArrayList<>();
        private final View target;
        private final View fromChild;
        private int scrollPos = 0;
        private boolean decelerateInEnd = false;

        private final Runnable updateRunnable = new Runnable() {
            @Override
            public void run() {
                if (!isEnd) {
                    if (scroller.computeScrollOffset()) {
                        Log.d("updateRunnable", "" + scroller.getCurrY());
                        int yu = scroller.getCurrY() - scrollPos;
                        scrollPos = scroller.getCurrY();
                        for (View v : scrollViews) {
                            if (yu == 0) {
                                break;
                            }
                            if (v == DoubanSlideView.this) {
                                int destOffset = fgYOffset - yu;
                                if (destOffset < fgMinYOffset) {
                                    destOffset = fgMinYOffset;
                                } else if (destOffset > fgMaxYOffset) {
                                    destOffset = fgMaxYOffset;
                                }
                                int c = fgYOffset - destOffset;
                                fgYOffset = destOffset;
                                yu -= c;
                                updateChildOffset();
                            } else {
                                if (v instanceof MyRecyclerView) {
                                    MyRecyclerView mrv = (MyRecyclerView) v;
                                    int beforeY = mrv.getScrollY2();
                                    mrv.scrollBy(0, yu);
                                    int afterY = mrv.getScrollY2();
                                    int c = afterY - beforeY;
                                    yu -= c;
                                } else {
                                    int beforeY = v.getScrollY();
                                    v.scrollBy(0, yu);
                                    int afterY = v.getScrollY();
                                    int c = afterY - beforeY;
                                    yu -= c;
                                }
                            }
                        }
                        target.post(this);
                    } else {
                        end();
                    }
                }
            }
        };

        public FlingHandler(Context context, View target, List<View> scrollViews) {
            scroller = new Scroller(context);
            this.target = target;
            this.fromChild = getFromChild(target);
            this.scrollViews.addAll(scrollViews);
        }

        public void startFling(float velocityX, float velocityY) {
            int distance = calcScrollDistance(velocityY);
//            int distance = 8000;
            if (!decelerateInEnd) {
                distance += 4000;
            }
//            distance += 500;
            if (velocityY > 0) {
                //向上
                scroller.fling(
                        0,
                        0,
                        (int) velocityX,
                        (int) velocityY,
                        0,
                        0,
                        0,
                        distance);
            } else {
                scroller.fling(
                        0,
                        0,
                        (int) velocityX,
                        (int) velocityY,
                        0,
                        0,
                        -distance,
                        0);
            }
            target.post(updateRunnable);
        }

        public void end() {
            isEnd = true;
        }

        private int calcScrollDistance(float velocityY) {
            int result = 0;
            if (velocityY > 0) {
                //累计上划量
                int beforeScrollY;
                int afterScrollY;
                for (View v : scrollViews) {
                    if (v == DoubanSlideView.this) {
                        result += (fgYOffset - fgMinYOffset);
                    } else {
                        //获取View可上滚高度
                        if (v instanceof MyRecyclerView) {
                            MyRecyclerView mrv = (MyRecyclerView) v;
                            beforeScrollY = mrv.getScrollY2();
                            mrv.scrollBy(0, 100000);
                            afterScrollY = mrv.getScrollY2();
                            result += (afterScrollY - beforeScrollY);
                            mrv.scrollTo(mrv.getScrollX2(), beforeScrollY);
                        } else {
                            beforeScrollY = v.getScrollY();
                            v.scrollBy(0, 100000);
                            afterScrollY = v.getScrollY();
                            result += (afterScrollY - beforeScrollY);
                            v.scrollTo(v.getScrollX(), beforeScrollY);
                        }
                    }
                }
            } else if (velocityY < 0) {
                for (View v : scrollViews) {
                    if (v == DoubanSlideView.this) {
                        result += (fgMaxYOffset - fgYOffset);
                    } else {
                        if (v instanceof MyRecyclerView) {
                            result += ((MyRecyclerView) v).getScrollY2();
                        } else {
                            result += v.getScrollY();
                        }
                    }
                }
            }
            return result;
        }
    }


}
